﻿using SD.Common;
using SD.Infrastructure.EntityBase;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlamDunk.ValueObjects.Enums;
using SlamDunk.ValueObjects.Interfaces;

namespace SlamDunk.MES.Domain.Entities.PlanContxt
{
    /// <summary>
    /// 生产计划
    /// </summary>
    public class ProductionPlan : AggregateRootEntity, ICheckable
    {
        #region # 构造器

        #region 00.无参构造器
        /// <summary>
        /// 无参构造器
        /// </summary>
        protected ProductionPlan()
        {
            //初始化导航属性
            this.MaterialAsks = new HashSet<ProductionMaterialAsk>();

            //默认值
            this.Status = ProductionPlanStatus.Waiting;
            this.CheckStatus = CheckStatus.Unchecked;
        }
        #endregion

        #region 01.创建生产计划构造器
        /// <summary>
        /// 创建生产计划构造器
        /// </summary>
        /// <param name="planNo">生产计划编号</param>
        /// <param name="planName">生产计划名称</param>
        /// <param name="productionBatchNo">生产批号</param>
        /// <param name="expStartTime">预期开始时间</param>
        /// <param name="expEndTime">预期结束时间</param>
        /// <param name="earliestStartTime">最早开始时间</param>
        /// <param name="latestEndTime">最晚结束时间</param>
        /// <param name="productId">产品Id</param>
        /// <param name="bomId">物料清单Id</param>
        /// <param name="expGrossQuantity">预期毛产量</param>
        /// <param name="expNetQuantity">预期净产量</param>
        /// <param name="saleOrderId">销售订单Id</param>
        /// <param name="facilityId">工厂模型Id</param>
        /// <param name="priority">优先级</param>
        /// <param name="description">描述</param>
        public ProductionPlan(string planNo, string planName, string productionBatchNo, DateTime expStartTime, DateTime expEndTime, DateTime earliestStartTime, DateTime latestEndTime, Guid productId, Guid bomId, decimal expGrossQuantity, decimal expNetQuantity, Guid? saleOrderId, Guid? facilityId, int priority, string description)
            : this()
        {
            base.Number = planNo;
            base.Name = planName;
            this.ProductionBatchNo = productionBatchNo;
            this.ExpStartTime = expStartTime;
            this.ExpEndTime = expEndTime;
            this.EarliestStartTime = earliestStartTime;
            this.LatestEndTime = latestEndTime;
            this.ProductId = productId;
            this.BomId = bomId;
            this.ExpGrossQuantity = expGrossQuantity;
            this.ExpNetQuantity = expNetQuantity;
            this.SaleOrderId = saleOrderId;
            this.FacilityId = facilityId;
            this.Priority = priority;
            this.Description = description;

            //初始化关键字
            this.InitKeywords();
        }
        #endregion

        #endregion

        #region # 属性

        #region 生产批号 —— string ProductionBatchNo
        /// <summary>
        /// 生产批号
        /// </summary>
        public string ProductionBatchNo { get; private set; }
        #endregion

        #region 预期开始时间 —— DateTime ExpStartTime
        /// <summary>
        /// 预期开始时间
        /// </summary>
        public DateTime ExpStartTime { get; private set; }
        #endregion

        #region 预期结束时间 —— DateTime ExpEndTime
        /// <summary>
        /// 预期结束时间
        /// </summary>
        public DateTime ExpEndTime { get; private set; }
        #endregion

        #region 最早开始时间 —— DateTime EarliestStartTime
        /// <summary>
        /// 最早开始时间
        /// </summary>
        public DateTime EarliestStartTime { get; private set; }
        #endregion

        #region 最晚结束时间 —— DateTime LatestEndTime
        /// <summary>
        /// 最晚结束时间
        /// </summary>
        public DateTime LatestEndTime { get; private set; }
        #endregion

        #region 实际开始时间 —— DateTime? ActualStartTime
        /// <summary>
        /// 实际开始时间
        /// </summary>
        public DateTime? ActualStartTime { get; private set; }
        #endregion

        #region 实际结束时间 —— DateTime? ActualEndTime
        /// <summary>
        /// 实际结束时间
        /// </summary>
        public DateTime? ActualEndTime { get; private set; }
        #endregion

        #region 产品Id —— Guid ProductId
        /// <summary>
        /// 产品Id
        /// </summary>
        public Guid ProductId { get; private set; }
        #endregion

        #region 物料清单Id —— Guid BomId
        /// <summary>
        /// 物料清单Id
        /// </summary>
        public Guid BomId { get; private set; }
        #endregion

        #region 预期毛产量 —— decimal ExpGrossQuantity
        /// <summary>
        /// 预期毛产量
        /// </summary>
        public decimal ExpGrossQuantity { get; private set; }
        #endregion

        #region 预期净产量 —— decimal ExpNetQuantity
        /// <summary>
        /// 预期净产量
        /// </summary>
        public decimal ExpNetQuantity { get; private set; }
        #endregion

        #region 销售订单Id —— Guid? SaleOrderId
        /// <summary>
        /// 销售订单Id
        /// </summary>
        public Guid? SaleOrderId { get; private set; }
        #endregion

        #region 工厂模型Id —— Guid? FacilityId 
        /// <summary>
        /// 工厂模型Id
        /// </summary>
        public Guid? FacilityId { get; private set; }
        #endregion

        #region 优先级 —— int Priority
        /// <summary>
        /// 优先级
        /// </summary>
        /// <remarks>数值越大，优先级越高</remarks>
        public int Priority { get; private set; }
        #endregion

        #region 生产计划状态 —— ProductionPlanStatus Status
        /// <summary>
        /// 生产计划状态
        /// </summary>
        public ProductionPlanStatus Status { get; private set; }
        #endregion

        #region 异常原因 —— string ExceptionReason
        /// <summary>
        /// 异常原因
        /// </summary>
        public string ExceptionReason { get; private set; }
        #endregion

        #region 提审人账号 —— string SubmitterAccount
        /// <summary>
        /// 提审人账号
        /// </summary>
        public string SubmitterAccount { get; set; }
        #endregion

        #region 提审人姓名 —— string SubmitterName
        /// <summary>
        /// 提审人姓名
        /// </summary>
        public string SubmitterName { get; set; }
        #endregion

        #region 审核状态 —— CheckStatus CheckStatus
        /// <summary>
        /// 审核状态
        /// </summary>
        public CheckStatus CheckStatus { get; set; }
        #endregion

        #region 审核意见 —— string CheckedRemark
        /// <summary>
        /// 审核意见
        /// </summary>
        public string CheckedRemark { get; set; }
        #endregion

        #region 审核人账号 —— string CheckerAccount
        /// <summary>
        /// 审核人账号
        /// </summary>
        public string CheckerAccount { get; set; }
        #endregion

        #region 审核人姓名 —— string CheckerName
        /// <summary>
        /// 审核人姓名
        /// </summary>
        public string CheckerName { get; set; }
        #endregion

        #region 审核时间 —— DateTime? CheckedTime
        /// <summary>
        /// 审核时间
        /// </summary>
        public DateTime? CheckedTime { get; set; }
        #endregion

        #region 描述 —— string Description
        /// <summary>
        /// 描述
        /// </summary>
        public string Description { get; private set; }
        #endregion

        #region 导航属性 - 生产物料需求集 —— ICollection<ProductionMaterialAsk> MaterialAsks
        /// <summary>
        /// 导航属性 - 生产物料需求集
        /// </summary>
        public virtual ICollection<ProductionMaterialAsk> MaterialAsks { get; private set; }
        #endregion

        #endregion

        #region # 方法

        #region 修改生产计划 —— void UpdateInfo(string planName, DateTime expStartTime...
        /// <summary>
        /// 修改生产计划
        /// </summary>
        /// <param name="planName">生产计划名称</param>
        /// <param name="productionBatchNo">生产批号</param>
        /// <param name="expStartTime">预期开始时间</param>
        /// <param name="expEndTime">预期结束时间</param>
        /// <param name="earliestStartTime">最早开始时间</param>
        /// <param name="latestEndTime">最晚结束时间</param>
        /// <param name="productId">产品Id</param>
        /// <param name="bomId">物料清单Id</param>
        /// <param name="expGrossQuantity">预期毛产量</param>
        /// <param name="expNetQuantity">预期净产量</param>
        /// <param name="saleOrderId">销售订单Id</param>
        /// <param name="facilityId">工厂模型Id</param>
        /// <param name="priority">优先级</param>
        /// <param name="description">描述</param>
        public void UpdateInfo(string planName, string productionBatchNo, DateTime expStartTime, DateTime expEndTime, DateTime earliestStartTime, DateTime latestEndTime, Guid productId, Guid bomId, decimal expGrossQuantity, decimal expNetQuantity, Guid? saleOrderId, Guid? facilityId, int priority, string description)
        {
            #region # 验证

            if (this.CheckStatus == CheckStatus.Checking)
            {
                throw new InvalidOperationException("审核中的生产计划不可修改！");
            }
            if (this.CheckStatus == CheckStatus.Passed)
            {
                throw new InvalidOperationException("审核通过的生产计划不可修改！");
            }

            #endregion

            base.Name = planName;
            this.ProductionBatchNo = productionBatchNo;
            this.ExpStartTime = expStartTime;
            this.ExpEndTime = expEndTime;
            this.EarliestStartTime = earliestStartTime;
            this.LatestEndTime = latestEndTime;
            this.ProductId = productId;
            this.BomId = bomId;
            this.ExpGrossQuantity = expGrossQuantity;
            this.ExpNetQuantity = expNetQuantity;
            this.SaleOrderId = saleOrderId;
            this.FacilityId = facilityId;
            this.Priority = priority;
            this.Description = description;

            //初始化关键字
            this.InitKeywords();
        }
        #endregion

        #region 设置生产物料需求 —— void SetMaterialAsks(IEnumerable<ProductionMaterialAsk>...
        /// <summary>
        /// 设置生产物料需求
        /// </summary>
        /// <param name="materialAsks">生产物料需求集</param>
        public void SetMaterialAsks(IEnumerable<ProductionMaterialAsk> materialAsks)
        {
            #region # 验证

            if (this.CheckStatus == CheckStatus.Checking)
            {
                throw new InvalidOperationException("审核中的生产计划不可设置生产物料需求！");
            }
            if (this.CheckStatus == CheckStatus.Passed)
            {
                throw new InvalidOperationException("审核通过的生产计划不可设置生产物料需求！");
            }

            #endregion

            materialAsks = materialAsks?.ToArray() ?? new ProductionMaterialAsk[0];

            this.ClearMaterialAsks();
            if (materialAsks.Any())
            {
                this.MaterialAsks.AddRange(materialAsks);
                foreach (ProductionMaterialAsk materialAsk in materialAsks)
                {
                    materialAsk.ProductionPlan = this;
                }
            }
        }
        #endregion

        #region 提交审核 —— void Submit(string submitterAccount...
        /// <summary>
        /// 提交审核
        /// </summary>
        /// <param name="submitterAccount">提审人账号</param>
        /// <param name="submitterName">提审人姓名</param>
        public void Submit(string submitterAccount, string submitterName)
        {
            this.DoSubmit(submitterAccount, submitterName);
        }
        #endregion

        #region 审核 —— void Check(bool passed, string checkedRemark...
        /// <summary>
        /// 审核
        /// </summary>
        /// <param name="passed">是否通过</param>
        /// <param name="checkedRemark">审核意见</param>
        /// <param name="checkedAccount">审核人账号</param>
        /// <param name="checkedName">审核人姓名</param>
        public void Check(bool passed, string checkedRemark, string checkedAccount, string checkedName)
        {
            this.DoCheck(passed, checkedRemark, checkedAccount, checkedName);
        }
        #endregion

        #region 发布 —— void Release()
        /// <summary>
        /// 发布
        /// </summary>
        public void Release()
        {
            #region # 验证

            if (this.CheckStatus != CheckStatus.Passed)
            {
                throw new InvalidOperationException("未审核通过的生产计划不可发布！");
            }

            #endregion

            this.Status = ProductionPlanStatus.Released;
        }
        #endregion

        #region 取消 —— void Cancel(string reason)
        /// <summary>
        /// 取消
        /// </summary>
        /// <param name="reason">取消原因</param>
        public void Cancel(string reason)
        {
            #region # 验证

            if (this.Status != ProductionPlanStatus.Waiting)
            {
                throw new InvalidOperationException("只有等待状态的生产计划可以取消！");
            }

            #endregion

            this.Status = ProductionPlanStatus.Cancelled;
            this.ExceptionReason = reason;
        }
        #endregion

        #region 执行 —— void Execute()
        /// <summary>
        /// 执行
        /// </summary>
        public void Execute()
        {
            this.Status = ProductionPlanStatus.Executing;
            if (!this.ActualStartTime.HasValue)
            {
                this.ActualStartTime = DateTime.Now;
            }
        }
        #endregion

        #region 中止 —— void Abort(string reason)
        /// <summary>
        /// 中止
        /// </summary>
        /// <param name="reason">中止原因</param>
        public void Abort(string reason)
        {
            #region # 验证

            if (this.Status != ProductionPlanStatus.Executing)
            {
                throw new InvalidOperationException("只有执行中的生产计划可以中止！");
            }

            #endregion

            this.Status = ProductionPlanStatus.Aborted;
            this.ExceptionReason = reason;
        }
        #endregion

        #region 完成 —— void Complete()
        /// <summary>
        /// 完成
        /// </summary>
        public void Complete()
        {
            this.Status = ProductionPlanStatus.Completed;
            this.ActualEndTime = DateTime.Now;
        }
        #endregion

        #region 清空生产物料需求 —— void ClearMaterialAsks()
        /// <summary>
        /// 清空生产物料需求
        /// </summary>
        private void ClearMaterialAsks()
        {
            foreach (ProductionMaterialAsk materialAsk in this.MaterialAsks.ToArray())
            {
                this.MaterialAsks.Remove(materialAsk);
            }
        }
        #endregion

        #region 初始化关键字 —— void InitKeywords()
        /// <summary>
        /// 初始化关键字
        /// </summary>
        private void InitKeywords()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(base.Number);
            builder.Append(base.Name);
            builder.Append(this.ProductionBatchNo);
            builder.Append(this.Description);

            base.SetKeywords(builder.ToString());
        }
        #endregion

        #endregion
    }
}
